import {AnimationStr, ChangeUnitType, CombitionRet, MaterialInfo, RewardType} from "../type/Type";
import {ConfigKey} from "../constant/ConfigConstant";
import {Util} from "../utils/Util";
import {DataRouter} from "../frame/dataSource/DataRouter";
import {UnitController} from "../controller/UnitController";
import {GuiControllerOpt, TimeControllerOpt, UISkillObj} from "../controller/model/ControllerOpt";
import {TimeController} from "../controller/TimeController";
import {TriggerUtil} from "../utils/TriggerUtil";
import {GlobalEnv} from "../utils/GlobalEnv";
import {
    TriggerMethodType,
    AttributeEnum,
    AttrInterface,
    UnitType,
    WeightIDData
} from "../frame/dataSource/DataModel";
import {TaskController} from "../controller/TaskController";
import {AchievementController} from "../controller/AchievementController";
import {FinalKey} from "../constant/FinalConstant";
import Configer from "../frame/apiModel/config/Configer";
import {GoodController} from "../controller/GoodController";
import {DataRouterUtil} from "../utils/router/DataRouterUtil";
import MathUtil from "../frame/codeAdapter/MathUtil";
import {GuiController, OptType} from "../controller/GuiController";
import {Logger} from "../frame/apiModel/config/Logger";
import {TriggerFactory} from "../frame/TriggerFactory";
import {UnitModel} from "../frame/apiModel/adapterModel/model3/UnitModel";
import {PlayerModel} from "../frame/apiModel/adapterModel/model4/PlayerModel";
import {LocModel} from "../frame/apiModel/base/LocModel";
import {PlayerFactory} from "../frame/PlayerFactory";
import {CodeUtil} from "../frame/codeAdapter/CodeUtil";
import {PlayerDataKey, UnitDataKey} from "../frame/dataSource/DataSource";
import {ItemJson, UnitJson} from "../frame/apiModel/model/StoreJson";
import {ModelFactory} from "../frame/apiModel/adapterModel/model5/ModelFactory";
import {EffectModel} from "../frame/apiModel/adapterModel/model2/EffectModel";
import {ItemModel} from "../frame/apiModel/adapterModel/model3/ItemModel";
import UnitUtil from "../utils/UnitUtil";
import {AbilityUtil} from "./ability/AbilityUtil";
import AddPatroSkillModel from "./ability/AddPatroSkillModel";
import CallBackPetSkillModel from "./ability/CallBackPetSkillModel";
import CallPetSkillModel from "./ability/CallPetSkillModel";
import CatchPetSkillModel from "./ability/CatchPetSkillModel";
import ChangeBagSkillModel from "./ability/ChangeBagSkillModel";
import EngulfPetSkillModel from "./ability/EngulfPetSkillModel";
import EngulfSkillModel from "./ability/EngulfSkillModel";
import EvolutionSkillModel from "./ability/EvolutionSkillModel";
import ExpCatchPetSkillModel from "./ability/ExpCatchPetSkillModel";
import HatchPetSkillModel from "./ability/HatchPetSkillModel";
import HookSkillModel from "./ability/HookSkillModel";
import ItemLevelUpSkillModel from "./ability/ItemLevelUpSkillModel";
import ItemMustLevelUpSkillModel from "./ability/ItemMustLevelUpSkillModel";
import MustCatchPetSkillModel from "./ability/MustCatchPetSkillModel";
import OneClickCallPetSkillModel from "./ability/OneClickCallPetSkillModel";
import OneClickCallBackSkillModel from "./ability/OneClickCallBackSkillModel";
import OneClickDropSkillModel from "./ability/OneClickDropSkillModel";
import OneClickFixupSkillModel from "./ability/OneClickFixupSkillModel";
import OneClickGetSkillModel from "./ability/OneClickGetSkillModel";
import OneClickMixSkillModel from "./ability/OneClickMixSkillModel";
import OneClickSellSkillModel from "./ability/OneClickSellSkillModel";
import PhoenixSkillModel from "./ability/PhoenixSkillModel";
import PushSkillModel from "./ability/PushSkillModel";
import ReleasePetSkillModel from "./ability/ReleasePetSkillModel";
import RemovePatroSkillModel from "./ability/RemovePatroSkillModel";
import ResetGoodSkillModel from "./ability/ResetGoodSkillModel";
import ShowFightValueSkillModel from "./ability/ShowFightValueSkillModel";
import TransFirstItemSkillModel from "./ability/TransFirstItemSkillModel";
import {AbilityController} from "../controller/AbilityController";
import SealSkillModel from "./ability/SealSkillModel";


export function initAbilityEffectTrigger() {
    const abilityEffectDatas = DataRouter.getAbilityEffectDatas();
    for (let i = 0; i < abilityEffectDatas.length; i++) {
        const abilityEffectData = abilityEffectDatas[i];
        const effectId = abilityEffectData.id;
        if (effectId) {
        } else {
            Logger.toastWarn(`第${i}项技能效果表没有id,跳过该技能效果的注册`)
            continue;
        }
        DataRouter.setEffectByEffectId(effectId, abilityEffectData);
        const condition = abilityEffectData.triggerMethod;
        let abilityIds = [];
        const tempIds = DataRouter.getAbilityIdsByEffectId(effectId);
        if (tempIds) {
            abilityIds = tempIds;
        }
        switch (condition) {
            case TriggerMethodType.activeUseTrigger: {
                const callback = function (this: void, activeUnit: UnitModel, activeItem: ItemModel, targetUnit: UnitModel, targetItem: ItemModel, activePlayer: PlayerModel, targetPlayer: PlayerModel, spellId: string, spellLoc: LocModel) {
                    const effectLevel = getEffectLevel(activeUnit, abilityIds, effectId, spellId);
                    if (!CodeUtil.isNullOrZero(effectLevel)) {
                        TriggerUtil.useAbilityEffect(activeUnit, targetUnit, abilityEffectData, spellLoc, effectLevel);
                    }
                }
                TriggerFactory.getInstance().registerUnitFinishUseSpell(callback)
                break;
            }
            case TriggerMethodType.activeAllTrigger: {
                const callback = function (this: void, activeUnit: UnitModel, activeItem: ItemModel, targetUnit: UnitModel, targetItem: ItemModel, activePlayer: PlayerModel, targetPlayer: PlayerModel, spellId: string, spellLoc: LocModel) {
                    const effectLevel = getEffectLevel(activeUnit, abilityIds, effectId);
                    if (!CodeUtil.isNullOrZero(effectLevel)) {
                        TriggerUtil.useAbilityEffect(activeUnit, targetUnit, abilityEffectData, spellLoc, effectLevel);
                    }
                }
                TriggerFactory.getInstance().registerUnitFinishUseSpell(callback)
                break;
            }
            case TriggerMethodType.intervalTrigger: {
                const callback = function () {
                    PlayerFactory.getInstance().mapAllNotEndPlayingPlayer(
                        (player) => {
                            const activeUnit = GlobalEnv.getHero(player);
                            if (activeUnit) {
                                const effectLevel = getEffectLevel(activeUnit, abilityIds, effectId);
                                if (!CodeUtil.isNullOrZero(effectLevel)) {
                                    const spellLoc = activeUnit.loc;
                                    TriggerUtil.useAbilityEffect(activeUnit, undefined, abilityEffectData, spellLoc, effectLevel);
                                }
                            }

                            const pets = player.data[PlayerDataKey.pet];
                            const maxPetNum = Configer.getConfig(ConfigKey.maxPetNum);
                            for (let index = 0; index < maxPetNum; index ++) {
                                const pet = pets[index];
                                const effectLevel = getEffectLevel(pet, abilityIds, effectId);
                                if (!CodeUtil.isNullOrZero(effectLevel)) {
                                    const spellLoc = pet.loc;
                                    TriggerUtil.useAbilityEffect(pet, undefined, abilityEffectData, spellLoc, effectLevel);
                                }
                            }

                            const summoneds = player.data[PlayerDataKey.summoned];
                            for (let index = 0; index < summoneds.length; index ++) {
                                const summoned = summoneds[index];
                                const effectLevel = getEffectLevel(summoned, abilityIds, effectId);
                                if (!CodeUtil.isNullOrZero(effectLevel)) {
                                    const spellLoc = summoned.loc;
                                    TriggerUtil.useAbilityEffect(summoned, undefined, abilityEffectData, spellLoc, effectLevel);
                                }
                            }
                        }
                    )
                }
                let intervalEffectNum = 0;
                if (!CodeUtil.isNullOrZero(abilityEffectData.cool)) {
                    intervalEffectNum = abilityEffectData.cool;
                }
                const foreverPeriodic = Configer.getFinal(FinalKey.foreverPeriodic) as number;
                const timerOpt = new TimeControllerOpt(undefined, callback, foreverPeriodic);
                TimeController.getInstance().startTimer({time: intervalEffectNum, opt: timerOpt});
                break;
            }
            case TriggerMethodType.attackTrigger: {
                const callback = function (attacker: UnitModel, attacked: UnitModel, bCh: boolean, bHit: boolean) {
                    const activeUnit = attacker;
                    const targetUnit = attacked;
                    const effectLevel = getEffectLevel(activeUnit, abilityIds, effectId);
                    if (!CodeUtil.isNullOrZero(effectLevel)) {
                        const spellLoc = targetUnit.loc;
                        TriggerUtil.useAbilityEffect(activeUnit, targetUnit, abilityEffectData, spellLoc, effectLevel);
                    }
                }
                UnitUtil.attackCallBack.push(callback);
                break;
            }
            case TriggerMethodType.attackedTrigger: {
                const callback = function (attacker: UnitModel, attacked: UnitModel, bCh: boolean, bHit: boolean) {
                    const activeUnit = attacked;
                    const targetUnit = attacker;
                    const effectLevel = getEffectLevel(activeUnit, abilityIds, effectId);
                    if (!CodeUtil.isNullOrZero(effectLevel)) {
                        const spellLoc = targetUnit.loc;
                        TriggerUtil.useAbilityEffect(activeUnit, targetUnit, abilityEffectData, spellLoc, effectLevel);
                    }
                }
                UnitUtil.attackCallBack.push(callback);
                break;
            }
            case TriggerMethodType.waveTrigger: {
                // todo 需要自己实现一套广播机制
                break;
            }
            case TriggerMethodType.levelUpTrigger: {
                const callback = function (this: void, activeUnit: UnitModel, activePlayer: PlayerModel, level: number, upLevel: number) {
                    if (upLevel > 0) {
                        const targetUnit = activeUnit;
                        const effectLevel = getEffectLevel(activeUnit, abilityIds, effectId);
                        if (!CodeUtil.isNullOrZero(effectLevel)) {
                            const spellLoc = activeUnit.loc;
                            for (let i = 0; i < upLevel; i++) {
                                TriggerUtil.useAbilityEffect(activeUnit, targetUnit, abilityEffectData, spellLoc, effectLevel);
                            }
                        }
                    }
                }
                TriggerFactory.getInstance().registerUnitLevelUp(callback)
                break;
            }
            case TriggerMethodType.killTrigger: {
                const deadCallBack = function (this: void, killer: UnitModel, killed: UnitModel, killerPlayer: PlayerModel, killedPlayer: PlayerModel) {
                    if (killer) {
                    } else {
                        return;
                    }
                    const effectLevel = getEffectLevel(killer, abilityIds, effectId);
                    if (!CodeUtil.isNullOrZero(effectLevel)) {
                        const spellLoc = killed.loc;
                        TriggerUtil.useAbilityEffect(killer, killed, abilityEffectData, spellLoc, effectLevel);
                    }
                }
                TriggerFactory.getInstance().registerUnitDead(deadCallBack)
                break;
            }
            case TriggerMethodType.deadTrigger: {
                const deadCallBack = function (this: void, killer: UnitModel, killed: UnitModel, killerPlayer: PlayerModel, killedPlayer: PlayerModel) {
                    if (killer) {
                    } else {
                        return;
                    }
                    const effectLevel = getEffectLevel(killed, abilityIds, effectId);
                    if (!CodeUtil.isNullOrZero(effectLevel)) {
                        const spellLoc = killer.loc;
                        TriggerUtil.useAbilityEffect(killed, killer, abilityEffectData, spellLoc, effectLevel);
                    }
                }
                TriggerFactory.getInstance().registerUnitDead(deadCallBack)
                break;
            }
            case TriggerMethodType.chTrigger: {
                const callback = function (attacker: UnitModel, attacked: UnitModel, bCh: boolean, bHit: boolean) {
                    if (bCh) {
                        const activeUnit = attacker;
                        const targetUnit = attacked;
                        const effectLevel = getEffectLevel(activeUnit, abilityIds, effectId);
                        if (!CodeUtil.isNullOrZero(effectLevel)) {
                            const spellLoc = targetUnit.loc;
                            TriggerUtil.useAbilityEffect(activeUnit, targetUnit, abilityEffectData, spellLoc, effectLevel);
                        }
                    }
                }
                UnitUtil.attackCallBack.push(callback);
                break;
            }
            case TriggerMethodType.dodgeTrigger: {
                const callback = function (attacker: UnitModel, attacked: UnitModel, bCh: boolean, bHit: boolean) {
                    if (bHit) {
                    } else {
                        const activeUnit = attacked;
                        const targetUnit = attacker;
                        const effectLevel = getEffectLevel(activeUnit, abilityIds, effectId);
                        if (!CodeUtil.isNullOrZero(effectLevel)) {
                            const spellLoc = targetUnit.loc;
                            TriggerUtil.useAbilityEffect(activeUnit, targetUnit, abilityEffectData, spellLoc, effectLevel);
                        }
                    }
                }
                UnitUtil.attackCallBack.push(callback);
                break;
            }
        }
    }
}
function getEffectLevel(unit: UnitModel, abilityIds: string[], effectId: string, activeAbilityId?: string): number {
    let level = 0;
    const player = PlayerFactory.getInstance().getUnitOwner(unit);

    if (unit?.isValid()) {
    } else {
        return level
    }

    if (abilityIds) {
        for (let i = 0; i < abilityIds.length; i++) {
            const tempAbilityId = abilityIds[i];
            const abilityLevel = unit?.getAbilityLevel(tempAbilityId);
            const abilitysLevel = unit?.getAbilitysLevel(tempAbilityId);
            if (activeAbilityId) {
                if (activeAbilityId === tempAbilityId) {
                    if (abilityLevel > 0) {
                        return abilityLevel;
                    } else {
                        return abilitysLevel;
                    }
                }
            } else {
                if (!CodeUtil.isNullOrZero(abilityLevel)) {
                    const abilityData = DataRouter.getAbilityDataById(tempAbilityId);
                    const requires = abilityData?.requiredAchs;
                    if (requires) {
                        const temp = AchievementController.getInstance().getAchievementNum(player, requires);
                        if (!CodeUtil.isNullOrZero(temp)) {
                            return temp;
                        }
                        continue;
                    }
                    const requiresTech = abilityData?.Requires;
                    if (requiresTech) {
                        const temp = player.getTech(requiresTech);
                        if (!CodeUtil.isNullOrZero(temp)) {
                            return temp;
                        }
                        continue;
                    }

                    return abilityLevel;
                }
            }
        }
    }
    if (activeAbilityId) {
        return level;
    }
    const achievementIds = DataRouter.getAchievementIdsByEffectId(effectId);
    if (achievementIds) {
        for (let i = 0; i < achievementIds.length; i ++) {
            const achievementId = achievementIds[i];
            const achievementNum = AchievementController.getInstance().getAchievementNum(player, achievementId);
            if (!CodeUtil.isNullOrZero(achievementNum)) {
                const ret = AchievementController.getInstance().checkBeneficiary(unit, achievementId);
                if (ret) {
                    return achievementNum;
                }
            }
        }
    }

    if (!CodeUtil.isNullOrZero(level)) {
        return level;
    }

    const unitCombinations = UnitController.getInstance().getUnitCombinationsByUnit(unit);
    for (let i = 0; i < unitCombinations.length; i++) {
        const unitCombination = unitCombinations[i];
        CodeUtil.mapObj(unitCombination, (key, ret: CombitionRet) => {
            const effects = ret.effects;
            if (effects && effects.includes(effectId)) {
                level = 1;
                return true
            }
            return false;
        })
        if (!CodeUtil.isNullOrZero(level)) {
            break
        }
    }
    if (!CodeUtil.isNullOrZero(level)) {
        return level;
    }

    const goodCombinations = GoodController.getInstance().getGoodCombinationByUnit(unit);
    CodeUtil.mapObj(goodCombinations, (key, ret) => {
        const effects = ret.effects;
        if (effects && effects.includes(effectId)) {
            level = 1;
            return true
        }
        return false;
    })
    if (!CodeUtil.isNullOrZero(level)) {
        return level;
    }

    const unitAffix = unit.data[UnitDataKey.unitAffix];
    if (unitAffix) {
        for (let i = 0; i < unitAffix.length; i++) {
            const affix = unitAffix[i];
            const affixData = DataRouter.getAffixDatasByAffixID(affix);
            if (affixData) {
                const effects = affixData.effects;
                const ret = Util.checkInSplit(effects, effectId);
                if (ret >= 0) {
                    level = 1;
                    break;
                }
            }
        }
    }
    if (!CodeUtil.isNullOrZero(level)) {
        return level;
    }

    // const handBook = AchievementController.getInstance().getHandBook(player);
    // if (handBook) {
    //     const unitId = DataRouterUtil.getUnitStr(unit);
    //     const exp = handBook[unitId];
    //     if (exp) {
    //         const handBookData = DataRouter.getHandBookDataByUnitId(unitId);
    //         const effects = handBookData.skill;
    //         const { lv } = DataRouterUtil.getLvByExp(exp, handBookData);
    //         level = lv;
    //     }
    // }

    unit.mapItems(
        (item, index) => {
            const itemId = item.strId;
            const itemData = DataRouter.getItemDataByItemId(itemId);
            const effects = itemData?.effects;
            const ret = Util.checkInSplit(effects, effectId)
            if (ret >= 0) {
                level = 1;
                return true;
            }
        }
    )

    return level;
}
export function initUseAbilityTrigger() {
    new AddPatroSkillModel().init();
    new CallBackPetSkillModel().init();
    new CallPetSkillModel().init();
    new CatchPetSkillModel().init();
    new ChangeBagSkillModel().init();
    new EngulfPetSkillModel().init();
    new EngulfSkillModel().init();
    new EvolutionSkillModel().init();
    new ExpCatchPetSkillModel().init();
    new HatchPetSkillModel().init();
    new HookSkillModel().init();
    new ItemLevelUpSkillModel().init();
    new ItemMustLevelUpSkillModel().init();
    new MustCatchPetSkillModel().init();
    new OneClickCallBackSkillModel().init();
    new OneClickCallPetSkillModel().init();
    new OneClickDropSkillModel().init();
    new OneClickFixupSkillModel().init();
    new OneClickGetSkillModel().init();
    new OneClickMixSkillModel().init();
    new OneClickSellSkillModel().init();
    new PhoenixSkillModel().init();
    new PushSkillModel().init();
    new ReleasePetSkillModel().init();
    new RemovePatroSkillModel().init();
    new ResetGoodSkillModel().init();
    new ShowFightValueSkillModel().init();
    new TransFirstItemSkillModel().init();
    new SealSkillModel().init();
    const callback = function (this: void, activeUnit: UnitModel, activeItem: ItemModel, targetUnit: UnitModel, targetItem: ItemModel, activePlayer: PlayerModel, targetPlayer: PlayerModel, spellId: string, spellLoc: LocModel) {
        let fun;
        let success = true;

        const { success: tempSuccess, bUse }= AbilityController.getInstance().useAbilityModelByAbilityId(activeUnit, activeItem, targetUnit, targetItem, activePlayer, targetPlayer, spellId, spellLoc);
        if (bUse) {
            success = tempSuccess;
        } else {
            AbilityUtil.useUISkill(activePlayer, spellId);
        }

        if (success) {
            if (fun) {
                fun();
            }
            const abilityData = DataRouter.getAbilityDataById(spellId);
            if (abilityData) {
                const newestAttr = UnitUtil.getAttribute(activeUnit);
                const cr = newestAttr[AttributeEnum.coolingReduction];
                const cool = abilityData.coolDown;
                const currentCool = UnitUtil.getCoolDown(cr, cool);
                AbilityController.getInstance().setCoolNum(activeUnit, spellId, currentCool);

                const successTip = abilityData.success_tip;
                if (successTip) {
                    Logger.toastProduct(successTip, activePlayer)
                }
                const rewardId = abilityData.reward;
                if (rewardId) {
                    TaskController.getInstance().reward(activeUnit, targetUnit, RewardType.bag, rewardId)
                }

                const parent = abilityData._parent;
                const castNum = abilityData.DataA;
                const generalMagic: string = Configer.getFinal(FinalKey.generalMagic);
                if (parent === generalMagic) {
                    if (!CodeUtil.isNullOrZero(castNum)) {
                        showCastBar(activeUnit, castNum, AnimationStr.spell);
                    }
                }
                // const newestAttr = UnitController.getInstance().getAttribute(activeUnit, AttrType.newestAttr);
                // const coolingReduction = newestAttr[AttributeEnum.coolingReduction];
                // if (coolingReduction && coolingReduction > 0) {
                //     const percentNum = coolingReduction / 100;
                //     const ability = DataRouter.getAbilityDataById(strId);
                //     const cooling = ability.Cool;
                //     if (cooling && cooling > 0) {
                //         const newCooling = cooling - (cooling * percentNum);
                //     }
                // }
            }
        } else {
            const abilityData = DataRouter.getAbilityDataById(spellId);
            if (abilityData) {
                const cool = abilityData.coolDown;
                if (!CodeUtil.isNullOrZero(cool)) {
                    activeUnit.replaceAbility(spellId, spellId)
                }
            }
        }
    }
    TriggerFactory.getInstance().registerUnitFinishUseSpell(callback);
}
export function initBeforeAbilityTrigger() {
    const fun = function (this: void, activeUnit: UnitModel, targetUnit: UnitModel, activePlayer: PlayerModel, targetPlayer: PlayerModel, spellId: string, spellLoc: LocModel) {
        const abilityData = DataRouter.getAbilityDataById(spellId);
        if (abilityData) {
            const parent = abilityData._parent;
            const castNum = abilityData.Cast;
            const generalMagic: string = Configer.getFinal(FinalKey.generalMagic);
            if (parent === generalMagic) {
                if (!CodeUtil.isNullOrZero(castNum)) {
                    showCastBar(activeUnit, castNum, AnimationStr.attack);
                }
            }
        }
    }
    TriggerFactory.getInstance().registerUnitBeforeUseSpell(fun)
}
function showCastBar(activeUnit: UnitModel, castNum: number, action: AnimationStr) {
    const player = PlayerFactory.getInstance().getUnitOwner(activeUnit);
    const castingStrip: string = Configer.getFinal(FinalKey.castingStrip);
    const loc = activeUnit.loc;
    let castBar: UnitModel;
    const unitJson: UnitJson = { id: castingStrip, e: 0 };
    const group = UnitController.getInstance().createUnit(1, unitJson, player, loc, 0)
    castBar = group[0];
    const unitId = activeUnit.strId;
    const unitData = DataRouter.getUnitDataByUnitId(unitId);

    let castBarScale = 1;
    if (unitData?.castBarScale) {
        castBarScale = unitData.castBarHeight;
    }
    let castBarHeight = 300;
    if (unitData?.castBarHeight) {
        castBarHeight = unitData.castBarHeight;
    }
    castBar.setScale(castBarScale, castBarScale, castBarScale)
    castBar.setAnimation(action)
    castBar.setTimeScale(10.00 / castNum)
    castBar.setflyHeight(castBarHeight, 0)
    const delCallBack = function () {
        UnitController.getInstance().unitDeadRemove(castBar);
    }
    const selfTimerOpt = new TimeControllerOpt(undefined, delCallBack, 1);
    TimeController.getInstance().startTimer({time: castNum, opt: selfTimerOpt});
}
